package org.xx.armory.spring5.mvc;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonView;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ser.FilterProvider;
import com.fasterxml.jackson.databind.ser.std.DateSerializer;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.validation.BindingResult;
import org.springframework.web.servlet.view.json.AbstractJackson2View;
import org.xx.armory.commons.IntegerEnum;
import org.xx.armory.commons.StringEnum;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import static org.xx.armory.commons.JsonUtils.BigDecimalJsonSerializer;
import static org.xx.armory.commons.JsonUtils.IntegerEnumJsonSerializer;
import static org.xx.armory.commons.JsonUtils.StringEnumJsonSerializer;
import static org.xx.armory.spring5.mvc.ModelUtils.SINGLE_BEAN_NAME;

/**
 * 将对象映射为Json字符串。
 *
 * <p>从映射结果中去掉 {@link BindingResult}, {@link JsonView} 和 {@link FilterProvider}。如果最终结果只包含一个值，并且键是 {@link ModelUtils#SINGLE_BEAN_NAME}，那么仅抽取该值映射为Json字符串。</p>
 *
 * @see ModelUtils#SINGLE_BEAN_NAME
 * @see ModelUtils#JSON_VIEW
 */
public class ArmoryMappingJackson2JsonView
        extends AbstractJackson2View {
    public static final String DEFAULT_CONTENT_TYPE = "application/json";

    public ArmoryMappingJackson2JsonView() {
        this(Jackson2ObjectMapperBuilder
                     .json()
                     .serializationInclusion(JsonInclude.Include.NON_NULL)
                     .serializerByType(Date.class, DateSerializer.instance.withFormat(true, null))
                     .serializerByType(IntegerEnum.class, new IntegerEnumJsonSerializer())
                     .serializerByType(StringEnum.class, new StringEnumJsonSerializer())
                     .serializerByType(BigDecimal.class, new BigDecimalJsonSerializer())
                     .build());
    }

    public ArmoryMappingJackson2JsonView(
            ObjectMapper objectMapper
    ) {
        super(objectMapper, DEFAULT_CONTENT_TYPE);

        // 禁止缓存JSON响应。
        setDisableCaching(true);
    }

    @Override
    public void setModelKey(
            String modelKey
    ) {
        // 忽视 modelKey 属性。
    }

    @Override
    protected Object filterModel(
            Map<String, Object> model
    ) {
        final var result = new HashMap<String, Object>(model.size());
        model.forEach((key, value) -> {
            if (!(value instanceof BindingResult)
                    && !key.equals(JsonView.class.getName())
                    && !key.equals(FilterProvider.class.getName())) {
                result.put(key, value);
            }
        });

        if (result.size() == 1 && result.containsKey(SINGLE_BEAN_NAME)) {
            return result.values().iterator().next();
        } else {
            return result;
        }
    }
}
